﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Net.Mail;
using System.Threading;

namespace Common
{
    public class Email
    {
        #region internal classes
        public class EmailSender
        {
            public EmailSender(string name, string address, string password)
            {
                Name = name;
                Address = address;
                Password = password;
            }

            private string _Name;
            public virtual string Name
            {
                get { return _Name; }
                set { _Name = value; }
            }

            private string _Address;
            public virtual string Address
            {
                get { return _Address; }
                set { _Address = value; }
            }

            private string _Password;
            public virtual string Password
            {
                get { return _Password; }
                set { _Password = value; }
            }
        }
        public class EmailServer
        {
            public EmailServer(string smtpAddress, int portNumber, bool enableSSL)
            {
                SMTP = smtpAddress;
                PortNumber = portNumber;
                EnableSSL = enableSSL;
            }

            private string _SMTP;
            public virtual string SMTP
            {
                get { return _SMTP; }
                set { _SMTP = value; }
            }

            private int _PortNumber;
            public virtual int PortNumber
            {
                get { return _PortNumber; }
                set { _PortNumber = value; }
            }

            private bool _EnableSSL;
            public virtual bool EnableSSL
            {
                get { return _EnableSSL; }
                set { _EnableSSL = value; }
            }
        }
        #endregion

        #region instances of internal classes
        public EmailSender Sender { get; set; }
        public EmailServer Server { get; set; }
        #endregion

        #region Constructors

        public Email()
        {
            
        }

        public Email(string emisor, string direccion, string contraseña, string smtp, int puerto, bool activarSSL)
        {
            Sender  = new EmailSender(emisor, direccion, contraseña);
            Server  = new EmailServer(smtp, puerto, activarSSL);
        }
        
        #endregion

        #region Public Methods

        // TODO => improve this!
        public static bool ValidateEmailAddress(string address)
        {
            return address.IndexOf("@", System.StringComparison.Ordinal) > 0;
        }

        public bool SendMail(Dictionary<string, string> recipients, string subject, string body, AsyncSendMailCompletedObject asyncSendMailCompletedObject = null)
        {
            var result = false;
            try
            {
                if (recipients.Count > 0)
                {
                    var encoding = System.Text.Encoding.UTF8;

                    var mailMessage = new MailMessage
                    {
                        From = new MailAddress(this.Sender.Address, this.Sender.Name, encoding),
                        Subject = subject,
                        SubjectEncoding = encoding,
                        IsBodyHtml = true,
                        Body = body
                    };
                    mailMessage.AddRecipients( recipients, encoding );

                    var smtpClient = new SmtpClient
                    {
                        UseDefaultCredentials = false,
                        Credentials = new System.Net.NetworkCredential(this.Sender.Address, this.Sender.Password),
                        Host = this.Server.SMTP,
                        Port = this.Server.PortNumber,
                        EnableSsl = this.Server.EnableSSL
                    };
                    
                    try
                    {
                        if ( asyncSendMailCompletedObject == null )
                        {
                            smtpClient.Send( mailMessage );
                        }
                        else
                        {
                            asyncSendMailCompletedObject.Mail = mailMessage;
                            smtpClient.SendCompleted += new SendCompletedEventHandler(SendMailCompletedCallback);
                            Thread.Sleep(2000);
                            smtpClient.SendAsync( mailMessage, asyncSendMailCompletedObject );
                        }
                    }
                    catch (SmtpFailedRecipientsException)
                    {
                        throw;
                    }
                    catch (SmtpException)
                    {
                        throw;
                    }
                    catch (Exception)
                    {
                        throw;
                    }
                    result = true;
                }
            }
            catch (Exception)
            {
                throw;
            }
            return result;
        }

        public class AsyncSendMailCompletedObject : IDisposable
        {
            public MailMessage Mail { get; set; }
            public IDisposable Object { get; set; }
            public Action<bool, MailMessage, object, Action<Exception>> OnCompleted { get; set; }
            public Action<Exception> LogError;

            private bool _disposed;
            protected virtual void Dispose(bool disposing)
            {
                if (!this._disposed)
                {
                    if (disposing)
                    {
                        if (this.Object != null)
                        {
                            //dispose managed resources
                            this.Object.Dispose();
                        }
                    }
                }
                //dispose unmanaged resources
                this._disposed = true;
            }

            public void Dispose()
            {
                Dispose(true);
                GC.SuppressFinalize(this);
            }
        }

        #endregion

        #region Private Methods

        private void SendMailCompletedCallback(object sender, AsyncCompletedEventArgs e)
        {
            if (e.Cancelled)
            {
                
            }
            if (e.Error.IsNotNull())
            {
                if (!String.IsNullOrEmpty(e.Error.Message))
                {
                    var message = e.Error.Message.ToLower();
                    if ( message.Contains("server committed a protocol violation") )
                    {

                    }
                    if ( message.Contains("service not available, closing transmission channel") )
                    {
                    }
                }
            }

            var mailSent = !e.Error.IsNotNull() && !e.Cancelled;

            var asyncSendMailCompletedInfo = (AsyncSendMailCompletedObject)e.UserState;
            if ( asyncSendMailCompletedInfo.IsNotNull() && asyncSendMailCompletedInfo.OnCompleted.IsNotNull() )
                asyncSendMailCompletedInfo.OnCompleted.Invoke( mailSent, asyncSendMailCompletedInfo.Mail, asyncSendMailCompletedInfo.Object, asyncSendMailCompletedInfo.LogError );

            // Dispose
            if (asyncSendMailCompletedInfo != null)
                asyncSendMailCompletedInfo.Dispose();
            ((SmtpClient)sender).Dispose();
        }

        #endregion
    }
}
